
#include "audio_system.h"

#include <iostream>
#include <sstream>

#include <boost/shared_ptr.hpp>

#include <fmod.hpp>
#include <fmod_errors.h>

#include "fmod_util.h"
#include "util.h"


namespace 
{
    void fmod_system_deleter(FMOD::System* s)
    {
        s->close();
        s->release();
    }
    
    void fmod_sound_deleter(FMOD::Sound* s) {
        if (s) {
            FMOD_RESULT fmRes = s->release();
            UNUSED_VARIABLE(fmRes);
        }
    }
    
    void fmod_channel_deleter(FMOD::Channel* c) {
        
    }
    
    AudioSystem::SystemPtr init_fmod(int deviceNumber = -1)
    {
	    FMOD_RESULT fmRes;
        
	    /* init fmod */
        
        FMOD::System* system_ptr;
	    fmRes = FMOD::System_Create(&system_ptr);
	    FmodErrThrow(fmRes);
        
        AudioSystem::SystemPtr system(system_ptr, &fmod_system_deleter);
        
        /* get version */
        
        unsigned int fmVersion;
	    fmRes = system->getVersion(&fmVersion);
	    FmodErrThrow(fmRes);
        
	    if(fmVersion < FMOD_VERSION) {
		    std::ostringstream s;
		    s << "old version of FMOD - " << std::hex << (int)fmVersion << "."
            << "This program requires " << std::hex << (int)FMOD_VERSION << "." << std::endl;
		    throw std::logic_error(s.str());
	    }
        
        if (deviceNumber >= 0) {
            fmRes = system->setDriver(deviceNumber);
            FmodErrThrow(fmRes);
        }
        
	    fmRes = system->init(32, FMOD_INIT_NORMAL, 0);
	    FmodErrThrow(fmRes);
        
        return system;
    }
}

AudioSystem::AudioSystem()
  : m_system(init_fmod())
{
    
}

AudioSystem::AudioSystem(const Config& config)
  : m_system(init_fmod(config.audioDeviceNumber()))
{
    
}

void AudioSystem::listDevices()
{
    FMOD_RESULT fmRes;
    int driverCount;
    
    fmRes = m_system->getNumDrivers(&driverCount);
    FmodErrThrow(fmRes);
    
    char driverName[100];
    const int nameLen = sizeof(driverName) / sizeof(driverName[0]);
    
    trace("Audio devices: ");
    
    for (int i=0; i<driverCount; ++i)
    {
        fmRes = m_system->getDriverInfo(i, driverName, nameLen, 0);
        FmodErrThrow(fmRes);            
        trace(" * device #" << i << ": " << driverName);
    }
}

AudioSystem::SoundPtr AudioSystem::createSound(const std::string& path, bool stream, AudioSystemKey)
{
    FMOD::Sound* sound_ptr = 0;
    FMOD_RESULT fmRes;
    
    const FMOD_MODE openMode = stream ? FMOD_CREATESTREAM : FMOD_CREATESAMPLE;
    fmRes = m_system->createSound(path.c_str(), openMode, 0, &sound_ptr);
    FmodErrThrow(fmRes);
    
    return SoundPtr(sound_ptr, &fmod_sound_deleter);
}

AudioSystem::ChannelPtr AudioSystem::playSound(const SoundPtr& sound, AudioSystemKey)
{
    FMOD_RESULT fmRes;
    FMOD::Channel* channel;
    
    fmRes = m_system->playSound(FMOD_CHANNEL_FREE, sound.get(), false, &channel);
    FmodErrThrow(fmRes);
    
    return ChannelPtr(channel, &fmod_channel_deleter);
}
